package com.wngbms.gatewaydemo.handler.bizhandler;

import com.wngbms.gatewaydemo.filter.HttpRequestFilter;
import com.wngbms.gatewaydemo.filter.HttpResponseFilter;
import com.wngbms.gatewaydemo.other.NamedThreadFactory;
import com.wngbms.gatewaydemo.router.HttpEndpointRouter;
import com.wngbms.gatewaydemo.router.RandomHttpEndpointRouter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpRequest;

import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;

public abstract class BaseDoHttpInboundHandler {

    protected List<String> backendUrls;
    protected ExecutorService proxyService;

    HttpResponseFilter httpResponseFilter;
    HttpEndpointRouter router = new RandomHttpEndpointRouter();

    protected BaseDoHttpInboundHandler(List<String> backends, HttpResponseFilter httpResponseFilter) {
        this.backendUrls = backends.stream().map(BaseDoHttpInboundHandler::formatUrl).collect(Collectors.toList());
        this.httpResponseFilter = httpResponseFilter;

        int cores = Runtime.getRuntime().availableProcessors();
        long keepAliveTime = 1000;
        int queueSize = 2048;

        RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
        proxyService = new ThreadPoolExecutor(cores, cores,
                keepAliveTime, TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(queueSize),
                new NamedThreadFactory("proxy-service"),
                handler);
    }

    public void handle(final FullHttpRequest fullHttpRequest,
                       final ChannelHandlerContext ctx,
                       HttpRequestFilter filter) {
        String backendUrl = router.route(this.backendUrls);
        String url = backendUrl + fullHttpRequest.uri();
        filter.filter(fullHttpRequest, ctx);

        proxyService.submit(() -> fetchGet(fullHttpRequest, ctx, url));
    }

    public abstract void fetchGet(FullHttpRequest request, ChannelHandlerContext ctx, String url);

    public abstract void handleResponse(FullHttpRequest fullHttpRequest,
                                ChannelHandlerContext ctx,
                                Object httpResponse);

    private static String formatUrl(String backend) {
        return backend.endsWith("/") ? backend.substring(0, backend.length() - 1) : backend;
    }

}
